全域執行環境被創造並執行時是放到執行堆去執行的,
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖
函數需要呼叫才會執行,函數在創造階段時就已經在記憶體中,函數被呼叫時會將函數的執行環境放到執行堆最上方(即是正在執行的程式,逐步、同步的),當執行完後函數的執行環境會離開執行堆最上方(回到全域執行環境)。
範例如下:
function b() {
}
function a() {
b();
}
a();
當函數 a 被呼叫,會產生一個新的執行環境並被放進執行堆裡,
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖
當函數 b 被呼叫,也會產生一個新的執行環境並被放進執行堆裡,會在執行堆最上方,表示正在執行的程式,
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖
再來看另外一個例子:
function a() {
b();
var c;
}
function b() {
var d;
}
a();
var d;
由於程式碼是逐行被解析的,
首先函數 a 會被呼叫,
因此函數 a 的執行環境會進入執行堆,
接著,它會變成目前執行的程式,
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖
最後一行的 var d 在函數 a 下一行 所以還不會被呼叫,
因為 JavaScript 是同步的(一次執行一行),
現在執行的程式就是目前執行堆裡最上方的執行環境,
所以當函數 a 的執行幻境在執行堆最上面時,它會逐行執行,
一路下來之後,呼叫函數 b ,
函數 b 會創造出自己的執行環境, 然後進入執行堆最上方,
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖
所以開始逐行執行,
呼叫函數 b,會在記憶體空間中設置變數 d 的初始值(undefined),
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖
接著回去執行函數 a ,替變數 c 設置在記憶體空間的位置,
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖
接著函數 b 的執行環境離開執行堆,
返回函數 a 的執行環境,現在函數 a 的執行環境在執行堆最上方(正在執行的程式),
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖
執行剛剛在執行環境,
還沒執行的那一行,
在記憶體空間設置 c 的位置,
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖
當函數 a 執行完畢,函數 a的執行環境離開執行堆,
執行全域執行環境中下一行還沒被執行的程式,
在全域執行環境中,在記憶體空間中設置變數 d 的初始值(undefined).
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖
知道如何運作了嗎?
每當函數被呼叫,
一個新的執行環境就被創造給函數,
this 變數被創造給那個函數,
裡面的變數在創造階段就已經建立了,
然後程式碼會被逐行執行,
但每當函數被呼叫,即使是被自己呼叫,
一個新的執行環境就會進入執行堆,
然後當執行完畢後,離開執行堆,
無論最上面是什麼,那個就是正在執行的程式,
逐行的、同步的,
這個觀念是非常重要的。
每個函數環境(執行環境)都會有自己的變數環境,
舉個例子來說,
程式碼如下:
function b() {
var myVar;
console.log(myVar);
}
function a() {
var myVar = 2;
console.log(myVar);
b();
}
var myVar = 1;
console.log(myVar);
a();
console.log(myVar); // 呼叫 a 函數後再次看 myVar 還是為1,因為這在全域執行環境執行
全域執行環境被創造,myVar
被放進記憶體中,
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 15 影片截圖
當呼叫 a
函數時,a
函數的執行環境被創造,
當它的執行環境被創造,myVar
會被放到執行環境的變數環境,
每個執行環境都有自己的變數環境,a
函數的執行環境的變數環境被創造 myVar
的值為2(在執行到 myVar
= 2那行程式時),
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 15 影片截圖
接著在呼叫 b 函數,一個執行環境和變數環境被創造,
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 15 影片截圖
這個變數也有自己的記憶體指向位置,因為他還沒有被設定為任何值,myVar
在記憶體中會指向 undefined
,
這和 scope 有關,這表示我們可以在哪裡看到變數,這裡的每個變數都被定義在自己的執行環境。
我們透過 Console 來看變數的值是不是跟我們想的一樣,
function b() {
var myVar;
console.log(myVar);
}
function a() {
var myVar = 2;
console.log(myVar);
b();
}
var myVar = 1;
console.log(myVar);
a();
在 Chrome 中 Console 結果:
如果我們在執行完 a 函數後在將 myVar 的值透過 console.log 顯示出來,
程式碼如下:
function b() {
var myVar;
console.log(myVar);
}
function a() {
var myVar = 2;
console.log(myVar);
b();
}
var myVar = 1;
console.log(myVar);
a();
console.log(myVar);
myVar 的值會從 1 被修改嗎?
我們可以打開 Chrome 看一下 Console 中的結果:
答案是不會!
因為函數 a 的執行環境已經離開執行堆,
回到全域執行環境,
執行堆裡只剩全域執行環境,
在全域執行環境的變數環境中 myVar 的值被設定為 1,
每個執行環境都有自己的變數環境,不會互相干擾。